Computer programs are instructions for controlling processors in computing devices. Computer programs are typically written in one or more high level languages that are easy for a human being to understand. These source language statements are then typically compiled by a special type of program called a compiler and converted to coded instructions, which often correspond to the actual operations performed by a processor in a computer device. Frequently the coded instructions are not identical to the native instructions for the processor, but, instead, are instructions for a particular virtual machine. A virtual machine is a process that interprets coded instructions and executes them. The virtual machine itself is an executable sequence of instructions in the native language of the processor. Virtual machines are sometimes called interpreters. As used herein, the term “machines” includes virtual machines interpreting virtual machine instructions, operating systems interpreting operating system instructions, and processors executing native instructions.
An advantage of a virtual machine is that the same coded instructions for a particular virtual machine may be used on multiple computer devices built with different processors supporting different native instructions. The particular virtual machine is formed separately, using instructions from a different native instruction set for each different processor. Then, any program written in or compiled to the coded instructions for the particular virtual machine can be executed on any processor that executes the particular virtual machine. Examples of virtual machines include the Java virtual machine (JVM), the BASIC interpreter, the VisualBasic interpreter, and the interpreter for the PL/SQL language of the Oracle Corporation.
When a program is written or modified, it may be compiled successfully into coded instructions, and yet still not perform correctly when executed by the virtual machine. To assist a programmer in determining how the program executes differently than expected, debuggers have been developed for virtual machines. Debuggers are processes that provide functions that enable a programmer to determine the contents of computer memory at a time of execution for any instruction in one or more sequences of instructions.
Virtual machine debuggers typically include a debugger client process that interacts with an interface of the virtual machine. The debugger client process executes on a user's computing device that includes a display device. The debugger client process typically presents information about high-level language statements, and the contents of memory when the statements are executed. The debugger client process also includes controls that the user can operate to indicate which memory contents to display, and to indicate the next set of one or more instructions to execute before reporting the contents of memory, or to indicate a particular instruction at which execution is stopped and memory contents reported. In response to user actions, the debugger client process interacts with the debugger interface in the virtual machine. For example, the debugger client process sends a message to the virtual machine requesting that the virtual machine execute the next instruction and return the contents of one or more memory locations. As another example, the debugger client process invokes one or more routines of the virtual machine to execute the next instruction. As used herein, the term “routine” refers to a sequence of instructions ending in a return to a calling entity. Other terms commonly used for routines include “functions,” “procedures,” “methods,” and “subroutines.” The main block of code that is first executed for a program that often returns control to an operating system is also a routine. The debugger client process often presents to the user the context of program execution using a stack of routines that have been called but have not yet returned control to the calling routine.
In general, coded instructions from one or more modules can be linked to form an executable program. Modules can consist of source language statements, coded instructions for a particular virtual machine, runtime executables, or some combination of these, with or without associated data. Modules in a high-level source language may be compiled by a run time compiler to produce corresponding modules in coded instructions.
It is becoming more common to create programs that include heterogeneous modules. For example, a program may include a machine-executable module, a module executable by a first type of virtual machine, and a module executable by a second type of virtual machine. During execution of the program, a routine in the machine-executable module may call a routine in the module running in a first type of virtual machine, and that routine may call another routine in the module running in a second type of virtual machine.
In addition, it is common for programs executing on different processors to interact with each other. For example, a database application program, such as an accounting program, runs on one processor on one device on a network. However, while running, the accounting program may cause a database procedure, such as an average salary computation procedure, to be launched by a database server and run on a second processor on a different device on the network. Even if the database application program and the stored procedure are implemented with coded instructions for the same type of virtual machine (e.g. both programs are Java programs), each processor is running a separate instance of the particular virtual machine. In many situations, the interacting programs will not only be running on different virtual machines, but will employ coded instructions for different types of virtual machines (e.g. one will be a Java program, while the other is a Visual BASIC program).
While computer programs now often include multiple modules executed by different virtual machines or different instances of the same virtual machine, the debuggers currently available are typically not designed to handle these situations. For example, while the human user thinks about the program as a single entity, the program's execution is typically displayed as a series of separate stacks, one stack for each instance of each virtual machine.
A programmer who wants to trace the contents of memory while executing a set of instructions that spans two modules run on separate virtual machines usually operates separate debugger clients for the two modules and interacts separately with the two instances of the virtual machine. Memory contents could be manually copied from the user interface of one debugger client and inserted in the user interface of a separate debugger client. The manual process is tedious and subject to increased risk of human errors. The tedium and risk of error are multiplied as the number of separate interacting modules that are to be debugged increases.
Based on the foregoing, there is a clear need for a debugger process that presents debug information in an intuitive manner even for programs that include multiple modules executing on multiple virtual machines. As used herein, “multiple virtual machines” refers to multiple instances of the same virtual machine, virtual machines for different languages, or both.